// rust所有权机制要求一个值只能有一个所有者,但有无法处理的情况:
// 1，图数据结构中,多个边可能会拥有同一个节点,该节点直到没有边指向它时,才应该被释放掉
// 2, 在多线程中,多个线程可能会持有同一个数据,但你受限于rust的安全机制,无法同时获取
// 该数据的可变引用.

pub fn learn_rcarc(){
  // _learn_ownership_error_example();
  // _learn_ownership_error_resolver_rc();
  _learn_watch_rc();
}

// Rc<T>
// 引用计数(reference counting),通过记录一个数据被引用的次数来确定该数据是否正在被
// 使用. 当引用次数归零时,就代表该数据不再被使用,因此可以被清理掉.
// Rc: 希望在堆上分配一个对象,供程序的多个部分使用,且无法确定哪个部分最后一个结束时.
// 就可以使用 Rc 成为数据值的所有者.

fn _learn_ownership_error_example(){
  let s = String::from("hello");
  // s 在这里被转移给a
  let a = Box::new(s);
  // 此处继续尝试将s转移给b
  // let b = Box::new(s);
  // error: use of moved value: `s` value used here after move
}

use std::rc::Rc;
fn _learn_ownership_error_resolver_rc(){
  // Rc::new, 创建了一个新的Rc<String>智能指针,并赋给变量a,该指针指向底层的字符串数据.
  let a = Rc::new(String::from("hello"));
  // Rc::new 或 Rc::clone 还会将引用计数+1, strong_count返回当前引用计数
  assert_eq!(1, Rc::strong_count(&a));
  // 这个clone不是深拷贝,仅仅复制了智能指针并增加了引用计数,没有克隆底层数据.
  // 即:a,b共享了底层的字符串 s.(还有其他clone也是浅拷贝,如迭代器)
  let b = Rc::clone(&a);
  assert_eq!(2, Rc::strong_count(&a));
  assert_eq!(Rc::strong_count(&a), Rc::strong_count(&b));
}

fn _learn_watch_rc(){
  let a = Rc::new(String::from("hello"));
  println!("count after creating a={}", Rc::strong_count(&a));
  let b = Rc::clone(&a);
  println!("count after creating b={}", Rc::strong_count(&a));
  {
    let c = Rc::clone(&a);
  println!("count after creating c={}", Rc::strong_count(&c));
  } // 自动调用drop, 引用计数-1, 因为Rc<T>也实现了Drop.
  println!("count after c goes out of space = {}", Rc::strong_count(&a));
}
// a,b,c超出作用域后,引用计数会变成0,最终智能指针和它指向的底层字符串都会被清理释放.

// 不可变引用
// Rc<T> 是指向底层数据的不可变引用,无法通过它来修改数据.这符合rust的借用规则:
// 要么存在多个不可变借用,要么只能存在一个可变借用

// 需要修改时,单独使用Rc<T>无法满足需求,需要配合其他数据类型来一起使用,例如内部可变性的
// RefCell<T>,以及互斥锁 Mutext<T>. 事实上,在多线程编程中, Arc跟Mutex锁的组合非常常见.
// 它们既可以让我们在不同的线程中共享数据,又允许在各个线程中对其进行修改.

// 一个综合例子

struct Owner {
  name: String,
}
struct Gadget {
  id: i32,
  owner: Rc<Owner>,
}

fn _learn_rc_example(){
  // 创建一个基于引用计数的'Owner'
  let gadget_owner:Rc<Owner>=Rc::new(Owner{
    name: "Gadget Man".to_string(),
  });
  // 创建两个不同的工具,它们属于同一个主人
  let gadget1 = Gadget{
    id:1,
    owner: Rc::clone(&gadget_owner),
  };
  let gadget2 = Gadget{
    id:2,
    owner: Rc::clone(&gadget_owner),
  };
  // 释放掉第一个 Rc<Owner>
  drop(gadget_owner);

  // 尽管上面释放了gadget_owner,但是依然可以在这了使用 owner 的信息.
  // 原因是在 drop 之前, 存在三个指向 Gadget Man 的智能指针引用, 上面仅仅
  // drop掉其中一个智能指针引用, 而不是 drop 掉 owner 数据, 外面还有两个引用
  // 指向底层的 owner 数据, 引用计数尚未清零
  // 因此 owner 数据依然可以被使用
  println!("Gadget {}, owned by {}", gadget1.id, gadget1.owner.name);
  println!("Gadget {}, owned by {}", gadget2.id, gadget2.owner.name);

  // 函数的最后,gadget1和gadget2 也被释放, 最终引用计数归零,随后底层数据也被清理释放
}

// Rc 简单总结
// 1, Rc/Arc 是不可变引用. 你无法修改它指向的值,只能进行读取,如果要修改,需要配合后面
// 的内部可变性 RefCell 或 互斥锁 Mutex
// 2, 一旦最后一个拥有者消失, 则资源会被自动回收, 这个生命周期是在编译期就确定下来的.
// 3, Rc 只能用于同一线程内部, 想要用于线程之间的对象共享, 需要使用Arc.
// 4, Rc<T> 是一个智能指针, 实现了Deref特征, 因此无需先解开Rc指针,再使用里面的T. 而是
// 可以直接使用T,例如上例中的 gadget1.owner.name

use std::thread;

// 多线程无力的 Rc<T>
fn _learn_thread_rc(){
  let s=Rc::new(String::from("hello"));
  for _ in 0..10 {
    let s = Rc::clone(&s);
    // error: Rc<T>不能在线程间安全的传递, 因其未实现Send特征
    // 更深层的原因: 由于Rc<T> 需要管理引用计数,但该计数器并没有使用任何
    // 并发原语,因此无法实现原子化的计数操作,最终会导致计数错误.
    /* 
    let handle = thread::spawn(move || {
      println!("{}", s)
    });*/
  }
}

// Arc
// Atomic Rc的缩写: 原子化的Rc<T>智能指针.
use std::sync::Arc;

fn _learn_thread_arc(){
  let s=Arc::new(String::from("hello"));
  for _ in 0..10 {
    let s = Arc::clone(&s);
    let handle = thread::spawn(move || {
      println!("{}", s)
    });
  }
}